/*******************************************************************************
** 文件名称：nz3801.c
** 文件作用：NZ3801操作
** 编写作者：Tom Free 付瑞彪
** 编写时间：2019-11-08
** 文件备注：
**
**
** 更新记录：
**          2019-11-08 -> 创建文件                          <Tom Free 付瑞彪>
**
**
**       Copyright (c) 深圳市三派智能科技有限公司 All Rights Reserved
**
**       1 Tab == 4 Spaces     UTF-8     ANSI C Language(C99)
*******************************************************************************/

#include "chip.h"
#include "gpio.h"
#include "spi.h"
#include "delay.h"
#include "nz3801.h"

/* 接收数据最大长度 */
#define RFID_MAX_RX_LENGTH      (18u)

/* 片选 */
#define RFID_CS_L()             do { PIN_OUT(PB, 2) = 0; } while (0)
#define RFID_CS_H()             do { PIN_OUT(PB, 2) = 1; } while (0)

/* 复位脚 */
#define RFID_RST_L()            do { PIN_OUT(PB, 1) = 0; } while (0)
#define RFID_RST_H()            do { PIN_OUT(PB, 1) = 1; } while (0)

/*******************************************************************************
** 函数名称：NZ3801_Init
** 函数作用：初始化端口
** 输入参数：无
** 输出参数：无
** 使用范例：NZ3801_Init();
** 函数备注：
*******************************************************************************/
void NZ3801_Init()
{
    GPIO_PinInit(PB, 1, GPIO_OUTPUT | GPIO_HIGH);
    SPI_Init(SPI_MSB, SPI_MASTER, SPI_MODE0, SPI_DIV8);
}

/*******************************************************************************
** 函数名称：NZ3801_RWByte
** 函数作用：读写一字节数据
** 输入参数：pRxByte - 数据地址，即可输入也可输出
** 输出参数：是否成功，GM_ERR - 失败，GM_OK - 成功
** 使用范例：GM_BOOL res = NZ3801_RWByte(&Data);
** 函数备注：
*******************************************************************************/
static GM_BOOL NZ3801_RWByte(GM_U8* pRxByte)
{
    *pRxByte = SPI_RWByte(*pRxByte);

    return GM_TRUE;
}

/*******************************************************************************
** 函数名称：NZ3801_ReadReg
** 函数作用：读取寄存器
** 输入参数：addr - 寄存器地址
** 输出参数：读出的值
** 使用范例：NZ3801_ReadReg();
** 函数备注：
*******************************************************************************/
static uint8_t NZ3801_ReadReg(uint8_t addr)
{
    uint8_t result = (addr << 1) | 0x80;

    RFID_CS_L();

    NZ3801_RWByte(&result);
    NZ3801_RWByte(&result);

    RFID_CS_H();

    return result;
}

/*******************************************************************************
** 函数名称：NZ3801_ReadReg
** 函数作用：读取寄存器
** 输入参数：addr - 寄存器地址
** 输出参数：读出的值
** 使用范例：NZ3801_ReadReg();
** 函数备注：
*******************************************************************************/
static void NZ3801_WriteReg(uint8_t addr, uint8_t value)
{
    addr = (addr << 1) & 0x7E;

    RFID_CS_L();

    NZ3801_RWByte(&addr);
    NZ3801_RWByte(&value);

    RFID_CS_H();
}

/*******************************************************************************
** 函数名称：NZ3801_SetBitMask
** 函数作用：设置某几位
** 输入参数：reg - 寄存器，mask - 设置掩码
** 输出参数：无
** 使用范例：NZ3801_SetBitMask();
** 函数备注：
*******************************************************************************/
static void NZ3801_SetBitMask(uint8_t reg, uint8_t mask)
{
    uint8_t tmp = 0x00;
    tmp = NZ3801_ReadReg(reg);
    NZ3801_WriteReg(reg, tmp | mask);
}

/*******************************************************************************
** 函数名称：NZ3801_ClearBitMask
** 函数作用：清除某几位
** 输入参数：reg - 寄存器，mask - 设置掩码
** 输出参数：无
** 使用范例：NZ3801_ClearBitMask();
** 函数备注：
*******************************************************************************/
static void NZ3801_ClearBitMask(uint8_t reg, uint8_t mask)
{
    uint8_t tmp = 0x00;
    tmp = NZ3801_ReadReg(reg);
    NZ3801_WriteReg(reg, tmp & (~mask));
}

/*******************************************************************************
** 函数名称：NZ3801_CalculateCRC
** 函数作用：计算CRC
** 输入参数：pIndata - 输入数据，len - 数据长度，pOutData - 输出数据
** 输出参数：无
** 使用范例：NZ3801_CalculateCRC();
** 函数备注：
*******************************************************************************/
static void NZ3801_CalculateCRC(uint8_t *pIndata, uint8_t len, uint8_t *pOutData)
{
    uint8_t i, n;
    NZ3801_ClearBitMask(DivIrqReg, 0x04);
    NZ3801_WriteReg(CommandReg, PCD_IDLE);
    NZ3801_SetBitMask(FIFOLevelReg, 0x80);
    for (i = 0; i < len; i++)
    {
        NZ3801_WriteReg(FIFODataReg, *(pIndata + i));
    }
    NZ3801_WriteReg(CommandReg, PCD_CALCCRC);
    i = 0xFF;
    do
    {
        n = NZ3801_ReadReg(DivIrqReg);
        i--;
    } while ((i != 0) && !(n & 0x04));

    pOutData[0] = NZ3801_ReadReg(CRCResultRegL);
    pOutData[1] = NZ3801_ReadReg(CRCResultRegM);
}

/*******************************************************************************
** 函数名称：NZ3801_ComWithCard
** 函数作用：和卡进行通信
** 输入参数：cmd - 命令
**           pInData - 发送到卡的数据
**           InLenByte - 发送字节数
**           pOutData - 接收卡片的数据
**           pOutLenBit - 返回的数据位长度
** 输出参数：是否成功
** 使用范例：uint8_t res = NZ3801_ComWithCard(cmd, &indata, 12, &outdata, &len);
** 函数备注：
*******************************************************************************/
static uint8_t NZ3801_ComWithCard(uint8_t cmd,
                                  uint8_t *pInData,
                                  uint8_t InLenByte,
                                  uint8_t *pOutData,
                                  uint16_t *pOutLenBit)
{
    uint8_t  status = MI_ERR;
    uint8_t  irqEn = 0x00;
    uint8_t  waitFor = 0x00;
    uint8_t  lastBits;
    uint8_t  n;
    uint16_t i;

    switch (cmd)
    {
    case PCD_AUTHENT:
        irqEn = 0x12;
        waitFor = 0x10;
        break;
    case PCD_TRANSCEIVE:
        irqEn = 0x77;
        waitFor = 0x30;
        break;
    default:
        break;
    }

    NZ3801_WriteReg(ComIEnReg, irqEn | 0x80);   //允许中断
    NZ3801_ClearBitMask(ComIrqReg, 0x80);       //清零中断标志位
    NZ3801_WriteReg(CommandReg, PCD_IDLE);      //唤醒
    NZ3801_SetBitMask(FIFOLevelReg, 0x80);      //清零缓冲区读写指针

    for (i = 0; i < InLenByte; i++)
    {
        NZ3801_WriteReg(FIFODataReg, pInData[i]);
    }
    NZ3801_WriteReg(CommandReg, cmd);           //设置工作模式，发送后自动接收

    if (cmd == PCD_TRANSCEIVE)
    {
        NZ3801_SetBitMask(BitFramingReg, 0x80);
    }   //数据发送，只在 PCD_TRANSCEIVE 模式有效

    i = 1000;   //根据时钟频率调整，操作M1卡最大等待时间25ms 600
    do
    {
        n = NZ3801_ReadReg(ComIrqReg);          //读取中断标志位
        i--;
    } while ((i != 0) && !(n & 0x01) && !(n&waitFor));  //ComIrqReg 0x01 TCounterValReg中的定时值减至 0 时置位
    //0x30  接收到有效数和指令执行完据置1
    NZ3801_ClearBitMask(BitFramingReg, 0x80);

    if (i != 0)
    {
        if (!(NZ3801_ReadReg(ErrorReg) & 0x1B))
        {
            status = MI_OK;                     //接收到数据，并且没有错误标志位置位
            if (n & irqEn & 0x01)
            {
                status = MI_NOTAGERR;
            }      //ComIrqReg 0x01 TCounterValReg中的定时值减至 0 时置位
            if (cmd == PCD_TRANSCEIVE)
            {
                n = NZ3801_ReadReg(FIFOLevelReg);     //读缓冲区字节数
                lastBits = NZ3801_ReadReg(ControlReg) & 0x07;
                if (lastBits)
                {
                    *pOutLenBit = (n - 1) * 8 + lastBits;
                }
                else
                {
                    *pOutLenBit = n * 8;
                }
                if (n == 0)
                {
                    n = 1;
                }
                if (n > RFID_MAX_RX_LENGTH)
                {
                    n = RFID_MAX_RX_LENGTH;
                }
                for (i = 0; i < n; i++)
                {
                    pOutData[i] = NZ3801_ReadReg(FIFODataReg);
                }
            }
        }
        else
        {
            status = MI_ERR;
        }

    }
    NZ3801_SetBitMask(ControlReg, 0x80);           // stop timer now
    NZ3801_WriteReg(CommandReg, PCD_IDLE);
    return status;
}

/*******************************************************************************
** 函数名称：NZ3801_AntennaOn
** 函数作用：开启天线
** 输入参数：无
** 输出参数：无
** 使用范例：NZ3801_AntennaOn();
** 函数备注：
*******************************************************************************/
void NZ3801_AntennaOn()
{
    /* 打开天线 */
    NZ3801_WriteReg(GsNReg, 0x44);
    NZ3801_WriteReg(CWGsCfgReg, 0x10);
    NZ3801_WriteReg(TxControlReg, 0x01);
}

/*******************************************************************************
** 函数名称：NZ3801_AntennaOff
** 函数作用：关闭天线
** 输入参数：无
** 输出参数：无
** 使用范例：NZ3801_AntennaOff();
** 函数备注：
*******************************************************************************/
void NZ3801_AntennaOff()
{
    NZ3801_ClearBitMask(TxControlReg, 0x03);
}

/*******************************************************************************
** 函数名称：NZ3801_Reset
** 函数作用：复位
** 输入参数：无
** 输出参数：是否OK
** 使用范例：NZ3801_Reset();
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_Reset()
{
    RFID_RST_H();
    asm("nop");
    RFID_RST_L();
    asm("nop");
    RFID_RST_H();
    asm("nop");
    NZ3801_WriteReg(CommandReg, PCD_SOFTRESET);    //设置软件掉电，关闭接收器模拟部分
    Delay_us(2200);
    NZ3801_WriteReg(Status2Reg, 0x08);    //加密和卡通信时的所有数据，支队Mifare有效
    NZ3801_WriteReg(ModeReg, 0x3D);     //和Mifare卡通讯，CRC初始值0x6363
    NZ3801_WriteReg(TReloadRegL, 30);
    NZ3801_WriteReg(TReloadRegH, 0);
    NZ3801_WriteReg(TModeReg, 0x8D);
    NZ3801_WriteReg(TPrescalerReg, 0x3E);
    NZ3801_WriteReg(TxAutoReg, 0x40);
    /* 2019-01-04 添加 */
    NZ3801_WriteReg(GsNReg, 0xFF);
    NZ3801_WriteReg(CWGsCfgReg, 0x3F);

    if (NZ3801_ReadReg(0x07) != 0x21)
    {
        return MI_ERR;
    }

    return MI_OK;
}

/*******************************************************************************
** 函数名称：NZ3801_Request
** 函数作用：寻卡
** 输入参数：req_code - 寻卡方式，pTagType - 卡类型
** 输出参数：是否OK
** 使用范例：NZ3801_Request(PICC_REQIDL, &type);
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_Request(uint8_t req_code, uint8_t *pTagType)
{
    uint8_t  status;
    uint16_t unLen;
    uint8_t  buf[20];

    NZ3801_ClearBitMask(Status2Reg, 0x08);
    NZ3801_WriteReg(BitFramingReg, 0x07);
    NZ3801_SetBitMask(TxControlReg, 0x03);
    buf[0] = req_code;

    status = NZ3801_ComWithCard(PCD_TRANSCEIVE, buf, 1, buf, &unLen);

    if ((status == MI_OK) && (unLen == 0x10))
    {
        *pTagType = buf[0];
        *(pTagType + 1) = buf[1];
    }
    else
    {
        status = MI_ERR;
    }
    return status;
}

/*******************************************************************************
** 函数名称：NZ3801_Anticoll
** 函数作用：防冲撞
** 输入参数：pSnr - 卡序列号
** 输出参数：是否OK
** 使用范例：NZ3801_Anticoll(&snr);
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_Anticoll(uint8_t *pSnr)
{
    uint8_t  status;
    uint8_t  i, snr_check = 0;
    uint16_t unLen;
    uint8_t  buf[RFID_MAX_RX_LENGTH];

    NZ3801_ClearBitMask(Status2Reg, 0x08);
    NZ3801_WriteReg(BitFramingReg, 0x00);
    NZ3801_ClearBitMask(CollReg, 0x80);

    buf[0] = PICC_ANTICOLL1;
    buf[1] = 0x20;

    status = NZ3801_ComWithCard(PCD_TRANSCEIVE, buf, 2, buf, &unLen);

    if (status == MI_OK)
    {
        for (i = 0; i < 4; i++)
        {
            *(pSnr + i) = buf[i];
            snr_check ^= buf[i];
        }
        if (snr_check != buf[i])
        {
            status = MI_ERR;
        }
    }

    NZ3801_SetBitMask(CollReg, 0x80);
    return status;
}

/*******************************************************************************
** 函数名称：NZ3801_Select
** 函数作用：选定卡片
** 输入参数：pSnr - 卡序列号
** 输出参数：是否OK
** 使用范例：NZ3801_Select(&snr);
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_Select(uint8_t *pSnr)
{
    uint8_t  status;
    uint8_t  i;
    uint16_t unLen;
    uint8_t  buf[RFID_MAX_RX_LENGTH];

    buf[0] = PICC_ANTICOLL1;
    buf[1] = 0x70;
    buf[6] = 0;
    for (i = 0; i < 4; i++)
    {
        buf[i + 2] = *(pSnr + i);
        buf[6] ^= *(pSnr + i);
    }
    NZ3801_CalculateCRC(buf, 7, &buf[7]);

    NZ3801_ClearBitMask(Status2Reg, 0x08);

    status = NZ3801_ComWithCard(PCD_TRANSCEIVE, buf, 9, buf, &unLen);

    if ((status == MI_OK) && (unLen == 0x18))
    {
        status = MI_OK;
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

/*******************************************************************************
** 函数名称：NZ3801_AuthState
** 函数作用：验证卡片密码
** 输入参数：auth_mode - 密码验证模式，0x60 - 验证A密钥，0x61 - 验证B密钥
**           block - 块地址
**           pKey - 密码
**           pSnr - 卡序列号
** 输出参数：是否OK
** 使用范例：NZ3801_AuthState(0x60, 2, &key, &snr);
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_AuthState(uint8_t auth_mode, uint8_t block, uint8_t *pKey, uint8_t *pSnr)
{
    uint8_t  status;
    uint16_t unLen;
    uint8_t  i, buf[RFID_MAX_RX_LENGTH];

    buf[0] = auth_mode;
    buf[1] = block;
    for (i = 0; i < 6; i++)
    {
        buf[i + 2] = *(pKey + i);
    }
    for (i = 0; i < 6; i++)
    {
        buf[i + 8] = *(pSnr + i);
    }

    status = NZ3801_ComWithCard(PCD_AUTHENT, buf, 12, buf, &unLen);
    if ((status != MI_OK) || (!(NZ3801_ReadReg(Status2Reg) & 0x08)))
    {
        status = MI_ERR;
    }

    return status;
}

/*******************************************************************************
** 函数名称：NZ3801_Read
** 函数作用：读取卡片数据
** 输入参数：block - 块地址
**           pData - 数据地址，长度16字节
** 输出参数：是否OK
** 使用范例：NZ3801_Read(10, &data);
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_Read(uint8_t block, uint8_t *pData)
{
    uint8_t  status;
    uint16_t unLen;
    uint8_t i, buf[RFID_MAX_RX_LENGTH];

    buf[0] = PICC_READ;
    buf[1] = block;
    NZ3801_CalculateCRC(buf, 2, &buf[2]);

    status = NZ3801_ComWithCard(PCD_TRANSCEIVE, buf, 4, buf, &unLen);
    if ((status == MI_OK) && (unLen == 0x90))
    {
        for (i = 0; i < 16; i++)
        {
            *(pData + i) = buf[i];
        }
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

/*******************************************************************************
** 函数名称：NZ3801_Write
** 函数作用：写卡片数据
** 输入参数：block - 块地址
**           pData - 数据地址，长度16字节
** 输出参数：是否OK
** 使用范例：NZ3801_Write(10, &data);
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_Write(uint8_t block, uint8_t *pData)
{
    uint8_t  status;
    uint16_t unLen;
    uint8_t  i, buf[RFID_MAX_RX_LENGTH];

    buf[0] = PICC_WRITE;
    buf[1] = block;
    NZ3801_CalculateCRC(buf, 2, &buf[2]);

    status = NZ3801_ComWithCard(PCD_TRANSCEIVE, buf, 4, buf, &unLen);

    if ((status != MI_OK) || (unLen != 4) || ((buf[0] & 0x0F) != 0x0A))
    {
        status = MI_ERR;
    }

    if (status == MI_OK)
    {
        for (i = 0; i < 16; i++)
        {
            buf[i] = *(pData + i);
        }
        NZ3801_CalculateCRC(buf, 16, &buf[16]);

        status = NZ3801_ComWithCard(PCD_TRANSCEIVE, buf, 18, buf, &unLen);
        if ((status != MI_OK) || (unLen != 4) || ((buf[0] & 0x0F) != 0x0A))
        {
            status = MI_ERR;
        }
    }

    return status;
}

/*******************************************************************************
** 函数名称：NZ3801_Halt
** 函数作用：进入休眠
** 输入参数：无
** 输出参数：是否OK
** 使用范例：NZ3801_Halt();
** 函数备注：
*******************************************************************************/
uint8_t NZ3801_Halt()
{
    uint8_t  status;
    uint16_t unLen;
    uint8_t  buf[RFID_MAX_RX_LENGTH];

    buf[0] = PICC_HALT;
    buf[1] = 0;
    NZ3801_CalculateCRC(buf, 2, &buf[2]);

    status = NZ3801_ComWithCard(PCD_TRANSCEIVE, buf, 4, buf, &unLen);

    return status;
}
